package com.binance.gateway.filter.time;

import com.binance.gateway.app.base.helper.BaseHelper;
import com.binance.gateway.app.constant.Constant;
import com.binance.gateway.constant.GwFilterOrders;
import com.binance.master.utils.WebUtils;
import com.ctrip.framework.apollo.ConfigService;
import com.netflix.util.Pair;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;

/**
 * 记录请求耗时
 */
@Component
@Slf4j
public class LogCostTimeFilter extends ZuulFilter {
    @Value("${clear.cookie:true}")
    private boolean clearCookie;
    @Resource
    private BaseHelper baseHelper;

    @Override
    public Object run() {
        try {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();

            String awsTrace = request.getHeader(Constant.X_AMZN_TRACE_ID);
            if (StringUtils.isNotBlank(awsTrace)) {
                try {
                    awsTrace = awsTrace.substring(awsTrace.indexOf("Root=") + 5);
                } catch (Exception e) { // Do nothing
                }
            }
            if (request != null && ctx.get("startTime") != null) {
                boolean isDev = StringUtils.equalsAnyIgnoreCase(System.getProperty("env"), "dev", "qa");
                // 默认不开启打印response
                String response = "skip";
                if (isDev || StringUtils.equals(ConfigService.getAppConfig().getProperty("log.response", "N"), "Y")) {
                    response = getResponse();
                }
                long cost = System.currentTimeMillis() - (long) ctx.get("startTime");
                if (cost > 10000) {
                    log.error("url={} response cost:{}", request.getRequestURL(), cost);
                } else if (cost > 5000) {
                    log.warn("url={} response cost={}", request.getRequestURL(), cost);
                }
                int responseStatus = getResponseStatus(ctx);
                String tokenSegment = BaseHelper.getTokenSegment(request);
                log.info(
                        "method={}, requestUrl={}, responseStatus={}, cost={}, X-UI-REQUEST-TRACE={}, x-amzn-trace-id={}, response={},userIP={},userId={},segment={}",
                        request.getMethod(), request.getRequestURL(), responseStatus, cost,
                        request.getHeader(Constant.HEADER_UI_REQUEST_TRACE), awsTrace, response,
                        WebUtils.getRequestIp(), BaseHelper.transformUserIdFromToken(tokenSegment), tokenSegment);

                // 401需要清空cookie
                if (clearCookie && responseStatus == 401) {
                    log.info("return 401 clear all cookie");
                    baseHelper.deleteAllCookiesWhileLogout();
                }
            } else {
                log.warn("request={} is null or ctxStartTime is null", request);
            }
        } catch (Exception e) {
            log.warn("打印耗时时间异常", e);
        }
        return null;
    }

    private String getResponse() {
        try {
            List<Pair<String, String>> headerList = RequestContext.getCurrentContext().getOriginResponseHeaders();
            String contentType = getContentType(headerList);
            if (StringUtils.equalsIgnoreCase("application/json;charset=UTF-8", contentType)) {
                InputStream stream = RequestContext.getCurrentContext().getResponseDataStream();
                String body = IOUtils.toString(stream, Charset.forName("UTF-8"));
                RequestContext.getCurrentContext().setResponseBody(body);
                return body;
            }
        } catch (IOException e) {
            log.warn("getResponse error");
        }
        return "notjsonskip";
    }

    private String getContentType(List<Pair<String, String>> headerList) {
        String contentType = null;
        for (Pair<String, String> pair : headerList) {
            if (StringUtils.equalsIgnoreCase(pair.first(), "Content-Type")) {
                contentType = pair.second();
            }
        }
        return contentType;
    }

    private int getResponseStatus(RequestContext ctx) {
        if (ctx.getResponse() != null) {
            return ctx.getResponse().getStatus();
        }
        log.info("ctx.getResponse() is null");
        return 200;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public int filterOrder() {
        return GwFilterOrders.LOG_END;
    }

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }
}
